package cmpe451.group7.ourcity.beans;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONObject;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.event.map.OverlaySelectEvent;
import org.primefaces.model.UploadedFile;
import org.primefaces.model.map.DefaultMapModel;
import org.primefaces.model.map.LatLng;
import org.primefaces.model.map.MapModel;
import org.primefaces.model.map.Marker;

import cmpe451.group7.ourcity.dao.MultimediaDAO;
import cmpe451.group7.ourcity.dao.PlaceDAO;
import cmpe451.group7.ourcity.dao.TagDAO;
import cmpe451.group7.ourcity.entity.Comment;
import cmpe451.group7.ourcity.entity.Multimedia;
import cmpe451.group7.ourcity.entity.MultimediaType;
import cmpe451.group7.ourcity.entity.Place;
import cmpe451.group7.ourcity.entity.Tag;
import cmpe451.group7.ourcity.entity.User;
import cmpe451.group7.ourcity.recommendation.FreebaseQuery;
import cmpe451.group7.ourcity.service.CommentService;
import cmpe451.group7.ourcity.service.PlaceService;
import cmpe451.group7.ourcity.service.UserService;
import cmpe451.group7.ourcity.util.GlobalVariables;
import cmpe451.group7.ourcity.util.Utils;

/**
 * Search bean for using in the search page of the web site.
 */
@ManagedBean
@ViewScoped
public class ViewPlaceBean implements Serializable {

	@ManagedProperty(value = "#{loginBean}")
	private LoginBean loginBean;

	@ManagedProperty(value = "#{commentService}")
	private CommentService commentService;

	@ManagedProperty(value = "#{userService}")
	private UserService userService;

	@ManagedProperty(value = "#{multimediaDAO}")
	private MultimediaDAO multimediaDAO;

	@ManagedProperty(value = "#{tagDAO}")
	private TagDAO tagDAO;

	@ManagedProperty(value = "#{placeDAO}")
	private PlaceDAO placeDAO;

	@ManagedProperty(value = "#{placeService}")
	private PlaceService placeService;

	private static final long serialVersionUID = 1L;

	private String placeIdValue;

	private MapModel map = new DefaultMapModel();
	private Place place;
	private int placeId;
	private Marker marker;
	private List<Comment> comments;
	private List<Comment> filteredComments;

	// for searching comments
	private String commentText;
	private List<Tag> commentTags;
	private Date toDate;
	private Date fromDate;

	// for adding new tags to the place
	private List<Tag> addedTags = new LinkedList<Tag>();

	// for adding videos to the system
	private String videoURL;

	// for adding audio to the system
	private String audioURL;
	private String audioTitle;

	// for adding comments to the place
	private String commentSearchText;
	private List<Tag> commentSearchTags;
	private Date commentDate;

	private User currentUser;

	public ViewPlaceBean() {
		super();

		Map<String, String> requestParameterMap = FacesContext
				.getCurrentInstance().getExternalContext()
				.getRequestParameterMap();

		if (requestParameterMap.keySet().size() > 1) {
			// TODO: error correction here
		}

		placeId = -1;

		try {
			placeIdValue = requestParameterMap.get("placeId");
			if (placeIdValue == null)
				Utils.redirectPage("search.xhtml");

			placeId = Integer.parseInt(placeIdValue);
		} catch (NumberFormatException e) {
			try {
				Utils.redirectPage("search.xhtml");
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	@PostConstruct
	public void init() {
		place = placeService.findAndFillTagsAndMultimediaId(placeId);

		if (loginBean != null && loginBean.isLoggedIn()) {
			currentUser = userService.fullyLoadUser(loginBean.getUserID());
		}

		if (place != null && place.getLatitude() != 0
				&& place.getLongitude() != 0) {
			// TODO: error correction here
			map.addOverlay(new Marker(new LatLng(place.getLatitude(), place
					.getLongitude()), place.getPlaceName()));
		}

		comments = commentService.findComments(place);
	}

	public void addComment() {
		if (loginBean == null || !loginBean.isLoggedIn()) {
			return;
		}

		if (StringUtils.isEmpty(commentText)) {
			Utils.addWarningMessage("Warning",
					"You have given an empty experience");
			return;
		}

		Date commentDate = this.commentDate == null ? new Date()
				: this.commentDate;
		Comment comment = commentService.createComment(place, currentUser,
				commentText, commentTags, commentDate);

		if (comment != null) {
			comments.add(comment);
			Utils.addInfoMessage("Success",
					"Your experience is successfully added");
		} else {
			Utils.addWarningMessage("Comment cannot be added",
					"An error occured in the server side while adding the comment");
		}

		commentText = null;
		commentTags = null;
		this.commentDate = null;
	}

	/**
	 * Adds new tags to the place
	 */
	public void addNewTags() {
		if (CollectionUtils.isEmpty(addedTags))
			return;

		for (Tag t : addedTags) {
			tagDAO.save(t);
			place.addTag(t);
		}

		placeDAO.update(place);

		addedTags = null;
	}

	public List<Tag> completeTags(String query) {
		return FreebaseQuery.completeTags(query);
	}

	/**
	 * Search the current comments
	 */
	public void searchComment() {
		if (StringUtils.isEmpty(commentSearchText) && fromDate == null
				&& toDate == null && CollectionUtils.isEmpty(commentSearchTags)) {
			FacesMessage msg = new FacesMessage("Warning",
					"Please enter a search field");
			displayMessage(msg);

			return;
		}

		comments = commentService.findByRestrictions(
				Long.parseLong(placeIdValue), commentSearchText, fromDate,
				toDate, commentSearchTags);
	}

	public LoginBean getLoginBean() {
		return loginBean;
	}

	public void setLoginBean(LoginBean loginBean) {
		this.loginBean = loginBean;
	}

	/**
	 * File upload event is handled here, used to upload images to the place
	 * 
	 * @param event
	 */
	public void handleFileUpload(FileUploadEvent event) {
		UploadedFile file = event.getFile();
		FacesMessage msg = null;

		Multimedia image = null;
		image = multimediaDAO.createMultimedia(currentUser, file.getContents(),
				file.getContentType(), MultimediaType.IMAGE);

		if (image != null) {
			place.addMultimedia(image);
			placeService.saveOrUpdate(place);

			String messageText = String.format(
					"%s (size:%d) is uploaded.\nContent type:%s",
					file.getFileName(), file.getSize(), file.getContentType());
			msg = new FacesMessage("Succesful", messageText);
		} else {
			String messageText = String.format(
					"Error occured while persisting %s (size:%d)",
					file.getFileName(), file.getSize());
			msg = new FacesMessage("Failure", messageText);
		}
		displayMessage(msg);
	}

	/**
	 * Adds an YouTube video to the system.
	 * 
	 * First it checks the validity of the url, and then extracts the relevant
	 * information from the response.
	 */
	public void addVideoURL() {
		if (StringUtils.isEmpty(videoURL)) {
			displayMessage(new FacesMessage("Failure",
					"Empty video url is given."));
			videoURL = null;
			return;
		}

		JSONObject videoProperties = Utils.youtubeVideoProperties(videoURL);
		if (videoProperties == null) {
			displayMessage(new FacesMessage("Failure",
					"Invalıd video url is given."));
			videoURL = null;
			return;
		}

		Multimedia video = new Multimedia();
		video.setMultimediaType(MultimediaType.VIDEO);
		video.setPath(videoURL);
		video.setTitle((String) videoProperties.get("title"));
		video.setOwner(currentUser);

		multimediaDAO.save(video);

		place.addMultimedia(video);
		placeDAO.update(place);

		displayMessage(new FacesMessage("Successful",
				"Video url is successfully added."));

		videoURL = null;
	}

	/**
	 * Adds an Vocaroo.com url to the place.
	 */
	public void addAudioURL() {
		if (StringUtils.isEmpty(audioURL)) {
			displayMessage(new FacesMessage("Failure",
					"Empty audio url is given."));
			return;
		}

		audioURL = audioURL.trim();

		boolean isValidAudioURL = GlobalVariables.audioURLPattern.matcher(
				audioURL).find();

		if (!isValidAudioURL) {
			displayMessage(new FacesMessage("Failure",
					"Invalıd audio url is given."));
			return;
		}

		Matcher matcher = GlobalVariables.audioIdPattern.matcher(audioURL);
		matcher.find();
		String audioPath = matcher.group(1);

		Multimedia audio = new Multimedia();
		audio.setMultimediaType(MultimediaType.AUDIO);
		audio.setPath(audioPath);
		audio.setTitle(audioTitle);
		audio.setOwner(currentUser);

		multimediaDAO.save(audio);

		place.addMultimedia(audio);
		placeDAO.update(place);

		displayMessage(new FacesMessage("Successful",
				"Audio url is successfully added."));

		audioURL = videoURL = null;
	}

	public List<Multimedia> getMediaList() {
		return new ArrayList<Multimedia>(place.getMultimedia());
	}

	/* getters & setters */

	public Place getPlace() {
		return place;
	}

	public void setPlace(Place place) {
		this.place = place;
	}

	public void displayMessage(FacesMessage message) {
		FacesContext.getCurrentInstance().addMessage(null, message);
	}

	public MapModel getMap() {
		return map;
	}

	public void setMap(MapModel map) {
		this.map = map;
	}

	public void onMarkerSelect(OverlaySelectEvent event) {
		marker = (Marker) event.getOverlay();
	}

	public Marker getMarker() {
		return marker;
	}

	public List<Comment> getComments() {
		return comments;
	}

	public void setComments(List<Comment> comments) {
		this.comments = comments;
	}

	public String getCommentText() {
		return commentText;
	}

	public void setCommentText(String commentText) {
		this.commentText = commentText;
	}

	public List<Tag> getCommentTags() {
		return commentTags;
	}

	public void setCommentTags(List<Tag> commentTag) {
		this.commentTags = commentTag;
	}

	public List<Comment> getFilteredComments() {
		return filteredComments;
	}

	public void setFilteredComments(List<Comment> filteredComments) {
		this.filteredComments = filteredComments;
	}

	public String getCommentSearchText() {
		return commentSearchText;
	}

	public void setCommentSearchText(String commentSearchText) {
		this.commentSearchText = commentSearchText;
	}

	public List<Tag> getCommentSearchTags() {
		return commentSearchTags;
	}

	public void setCommentSearchTags(List<Tag> commentSearchTags) {
		this.commentSearchTags = commentSearchTags;
	}

	public Date getToDate() {
		return toDate;
	}

	public void setToDate(Date toDate) {
		this.toDate = toDate;
	}

	public Date getFromDate() {
		return fromDate;
	}

	public void setFromDate(Date fromDate) {
		this.fromDate = fromDate;
	}

	public MultimediaDAO getMultimediaDAO() {
		return multimediaDAO;
	}

	public void setMultimediaDAO(MultimediaDAO multimediaDAO) {
		this.multimediaDAO = multimediaDAO;
	}

	public CommentService getCommentService() {
		return commentService;
	}

	public void setCommentService(CommentService commentService) {
		this.commentService = commentService;
	}

	public PlaceService getPlaceService() {
		return placeService;
	}

	public void setPlaceService(PlaceService placeService) {
		this.placeService = placeService;
	}

	public List<Tag> getAddedTags() {
		return addedTags;
	}

	public void setAddedTags(List<Tag> addedTags) {
		this.addedTags = addedTags;
	}

	public TagDAO getTagDAO() {
		return tagDAO;
	}

	public void setTagDAO(TagDAO tagDAO) {
		this.tagDAO = tagDAO;
	}

	public PlaceDAO getPlaceDAO() {
		return placeDAO;
	}

	public void setPlaceDAO(PlaceDAO placeDAO) {
		this.placeDAO = placeDAO;
	}

	public String getVideoURL() {
		return videoURL;
	}

	public void setVideoURL(String videoURL) {
		this.videoURL = videoURL;
	}

	public String getAudioURL() {
		return audioURL;
	}

	public void setAudioURL(String audioURL) {
		this.audioURL = audioURL;
	}

	public String getAudioTitle() {
		return audioTitle;
	}

	public void setAudioTitle(String audioTitle) {
		this.audioTitle = audioTitle;
	}

	public Date getCommentDate() {
		return commentDate;
	}

	public void setCommentDate(Date commentDate) {
		this.commentDate = commentDate;
	}

	public UserService getUserService() {
		return userService;
	}

	public void setUserService(UserService userService) {
		this.userService = userService;
	}
}
